package com.ease.util {
import flash.net.registerClassAlias;
import flash.utils.ByteArray;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

import mx.collections.ArrayCollection;


public class ObjectUtils {
	/**
	 * Base clone object
	 */
	public static function baseClone(source:*):*{
		if(!source||isString(source)||isNumber(source)||isBoolean(source))
			return source;
		var typeName:String = getQualifiedClassName(source);
        var packageName:String = typeName.split("::")[0];
        var type:Class = Class(getDefinitionByName(typeName));

        registerClassAlias(packageName, type);

        var copier:ByteArray = new ByteArray();
        copier.writeObject(source);
        copier.position = 0;
        return copier.readObject();
	}

	public static function cloneArrayCollection(source:ArrayCollection):ArrayCollection{
		var copier:ArrayCollection = new ArrayCollection();
		for each (var o:* in source){
			copier.addItem(deepClone(o));
		}
		return copier;
	}
	/**
	 * Deep clone object
	 */
	public static function deepClone(source:*):*{
		collection = new ArrayCollection();

		return cloneAlong(source);

	}

	private static function cloneAlong(source:*):*{
		if(!source||isString(source)||isNumber(source)||isBoolean(source))
			return source;

		if(source is XML)
			return XML(source.toXMLString());

		if(collection.contains(source))
			return baseClone(source);
		else collection.addItem(source);

		var targetClass:Class = Object(source).constructor;
		var instance: * = new targetClass();

		var classInfo:XML = describeType(source);
        var propertyName:String;
        var propertyValue:Object;
        // List the object's variables, their values, and their types.
        for each ( var v:XML in classInfo..variable )
        {
            propertyName = v.@name;
            propertyValue = source[propertyName];
            checkObjectTypeAndClone(propertyName , propertyValue , true , instance);
        }

        // List accessors as properties.
        for each ( var a:XML in classInfo..accessor )
        {
            // Do not user the property if it can't be read or written
            if( a.@access == 'readwrite' )
            {
                propertyName = a.@name;
                propertyValue = source[propertyName];
                checkObjectTypeAndClone(propertyName , propertyValue , true , instance);
            }

        }

        return instance;
	}

	private static function checkObjectTypeAndClone(propertyName:String, propertyValue:Object, deep:Boolean, instance:Object):void {
        if( propertyValue is Array ) {
            instance[propertyName] = cloneArray(propertyValue as Array , deep);
        }
        else if( propertyValue is ArrayCollection ) {
            instance[propertyName] = new ArrayCollection(cloneArray(ArrayCollection(propertyValue).source , deep));
        }
        else
        {
            instance[propertyName] = cloneAlong(propertyValue);
        }
    }

    private static function cloneArray(val:Array, deep:Boolean = false):Array{
        var clonedObj : Array = new Array;
        for( var i:int = 0 ; i < val.length ; i++ ){
            if( val[i] is Array )
            {
                clonedObj[i] = cloneArray(val[i] , deep);
            }
            else if( val[i] is ArrayCollection )
            {
                clonedObj[i] = new ArrayCollection(cloneArray(ArrayCollection(val[i]).source , deep));
            }
            else
            {
                clonedObj[i] = baseClone(val[i]);
            }
        }
        return clonedObj;
    }
	/**
	 * Checks wherever passed-in value is <code>String</code>.
	 */
	public static function isString(value:*):Boolean {
		return ( typeof(value) == "string" || value is String );
	}

	/**
	 * Checks wherever passed-in value is <code>Number</code>.
	 */
	public static function isNumber(value:*):Boolean {
		return ( typeof(value) == "number" || value is Number );
	}

	/**
	 * Checks wherever passed-in value is <code>Boolean</code>.
	 */
	public static function isBoolean(value:*):Boolean {
		return ( typeof(value) == "boolean" || value is Boolean );
	}

	/**
	 * Checks wherever passed-in value is <code>Function</code>.
	 */
	public static function isFunction(value:*):Boolean {
		return ( typeof(value) == "function" || value is Function );
	}

	private static var collection:ArrayCollection = new ArrayCollection();
}
}